import numpy as np  # 矩阵计算的函数库
import random
from selenium.webdriver import ActionChains  # 控制鼠标操作
from selenium.webdriver.chrome.options import Options  # 不展示浏览器窗口  测试不通过
import time
from selenium import webdriver
from PIL import Image  # Python图像库
import os
from selenium.webdriver.support.ui import WebDriverWait
import cv2  # 图像处理和计算机视觉库
import pymysql
import json
import sys


class Login(object):
    def __init__(self):
        # if len(sys.argv) < 3:
        #     exit("缺少参数 账号或密码")
        # 初始化方法：url、driver(浏览器驱动)
        # 添加账号和密码
        self.url = "https://open.yuewen.com/pub/public/login.html"

        # self.account = sys.argv[1]
        # self.password = sys.argv[2]
        self.account = "1131330901@qq.com"
        self.password = "Zwxs2020"
        chrome_options
        self.driver = webdriver.Chrome()
        self.index = 'https://open.yuewen.com/pub/index/index.html'
        self.agent = ''


    @staticmethod
    def show(name):
        # 调用窗口显示图片
        cv2.imshow("Show", name)  # 参数1：窗口名称；参数2：需要显示的图片
        cv2.waitKey(0)
        cv2.destroyAllWindows()

    @staticmethod
    def webdriverwait_send_keys(dri, element, value):

        WebDriverWait(dri, 10, 5).until(lambda dr: element).send_keys(value)

    @staticmethod
    def webdriverwait_click(dri, element):

        WebDriverWait(dri, 10, 5).until(lambda dr: element).click()
        # 当需要多次调用该显示等待函数时，可以使用匿名函数lambda来获取元素     lambda函数接收一个参数word(即上面的dr)。且在冒号和末尾圆括号之间的部分为函数的定义。

    @staticmethod
    def get_position(chunk, canves):
        """
        判断缺口位置
        :param chunk:   缺口图片(验证码中的大图)
        :param canves:  验证码中的拼图
        :return:        位置 x, y
        """
        # cv2.imread()用于读取图片文件；图片路径，读取图片的形式（1表示彩色图片[默认]，0表示灰度图片，-1表示原来的格式）
        chunk = cv2.imread(chunk, 0)  # 读取大图(灰化)
        canves = cv2.imread(canves, 0)  # 读取拼图(灰化)

        # 二值化后的图片名称
        slide_puzzle = "slide_puzzle.jpg"
        slide_bg = "slide_bg.jpg"
        # 将二值化后的图片进行保存
        # cv2.imwrite()用于保存图片文件；参数1：保存的图像名称，参数2：需要保存的图像
        cv2.imwrite(slide_bg, chunk)  # 保存大图
        cv2.imwrite(slide_puzzle, canves)  # 保存拼图

        chunk = cv2.imread(slide_bg)  # 使用cv2.imread()读出来的是BGR数据格式
        # cv2.cvtColor(p1, p2) 是颜色空间转换函数    参数1：需要转换的图片，参数2：转换成何种格式
        # cv2.COLOR_BGR2RGB:将BGR格式转换成RGB格式      cv2.COLOR_BGR2GRAY:将BGR格式转换成灰度图片
        chunk = cv2.cvtColor(chunk, cv2.COLOR_BGR2GRAY)

        chunk = abs(255 - chunk)
        cv2.imwrite(slide_bg, chunk)
        chunk = cv2.imread(slide_bg)  # 读取大图
        canves = cv2.imread(slide_puzzle)  # 读取拼图
        # 获取偏移量
        result = cv2.matchTemplate(chunk, canves, cv2.TM_CCOEFF_NORMED)
        y, x = np.unravel_index(result.argmax(), result.shape)
        return x

    @staticmethod
    def get_track(distance):
        """
        模拟轨迹 假装是人在操作
            匀变速运动基本公式：
                ①v=v0+at            速度公式
                ②s=v0t+(1/2)at²     位移公式

        :param distance:    移动的距离
        :return:            track
        """
        v = 0
        t = 0.2
        tracks = []
        current = 0
        mid = distance * 4 / 5
        distance += 10
        while current < distance:
            if current < mid:

                a = random.randint(28, 30)
            else:
                a = -random.randint(12, 13)

            v0 = v
            s = v0 * t + 0.5 * a * (t ** 2)
            current += s
            tracks.append(round(s))
            v = v0 + a * t

        for i in range(4):
            tracks.append(-random.randint(3, 4))
        return tracks

    @staticmethod
    def urllib_download(imgurl, imgsavepath):
        """
        下载图片
        :param imgurl:      需要下载图片的url
        :param imgsavepath: 图片存放位置
        """

        from urllib.request import urlretrieve
        urlretrieve(imgurl, imgsavepath)

    def after_quit(self):
        """
        关闭浏览器
        """
        self.driver.quit()

    def login_main(self):
        # ssl._create_default_https_context = ssl._create_unverified_context
        driver = self.driver
        driver.maximize_window()
        driver.get(self.url)
        driver.find_element_by_xpath("//*[@id='accountInput']").send_keys(self.account)
        driver.find_element_by_xpath('//*[@id="passwordInput"]').send_keys(self.password)

        # 获取到元素并实现点击
        click_keyi_username = driver.find_element_by_xpath('//*[@id="submitBtn"]')
        self.webdriverwait_click(driver, click_keyi_username)

        time.sleep(1)

        driver.switch_to.frame(driver.find_element_by_id('tcaptcha_iframe'))
        time.sleep(0.5)

        bk_block = driver.find_element_by_xpath('//*[@id="slideBg"]')
        web_image_width = bk_block.size

        web_image_width = web_image_width['width']

        # print(bk_block.location)
        bk_block_x = bk_block.location['x']

        slide_block = driver.find_element_by_xpath('//img[@id="slideBlock"]')  # 获取验证码中的拼图
        # print(bk_block.location)                  # 该图片对象在弹出的验证码框中的位置，返回字典的格式，例如：{'x': 36, 'y': 102}
        slide_block_x = slide_block.location['x']  # 获取该图片对象在验证码框中的位置(x轴)

        bk_block = driver.find_element_by_xpath('//img[@id="slideBg"]').get_attribute('src')  # 获取验证码中的大图url
        slide_block = driver.find_element_by_xpath('//img[@id="slideBlock"]').get_attribute('src')  # 获取验证码中的拼图url
        slid_ing = driver.find_element_by_xpath('//div[@id="tcaptcha_drag_thumb"]')  # 获取滑块

        os.makedirs('./image/', exist_ok=True)
        self.urllib_download(bk_block, './image/bkBlock.png')
        self.urllib_download(slide_block, './image/slideBlock.png')
        time.sleep(0.2)

        img_bkblock = Image.open('./image/bkBlock.png')
        real_width = img_bkblock.size[0]
        width_scale = float(real_width) / float(web_image_width)
        position_x = self.get_position('./image/bkBlock.png',
                                       './image/slideBlock.png')  # 获取到 大图 与 拼图 位移的距离 (实际滑动的距离就是x轴的距离)
        real_position = position_x / width_scale  # 将大图/比例，得到验证码框中大图与拼图实际的滑动距离

        real_position = real_position - (
                    slide_block_x - bk_block_x)  # (slide_block_x - bk_block_x):即拼图到大图的左边距，所以减去左边距后才得到真正的滑动距离

        track_list = self.get_track(real_position)  # 调用get_track()方法，传入真实距离参数，得出移动轨迹

        ActionChains(driver).click_and_hold(on_element=slid_ing).perform()  # 找到滑块元素，点击鼠标左键，按住不放
        time.sleep(0.2)
        # 拖动元素
        for track in track_list:
            ActionChains(driver).move_by_offset(xoffset=track, yoffset=0).perform()  # 根据运动轨迹(x轴)，进行拖动
            time.sleep(0.002)
        time.sleep(1)
        ActionChains(driver).release(on_element=slid_ing).perform()  # 释放鼠标

        time.sleep(2)

        # curent_url = self.driver.current_url
        # if(curent_url != self.index):
        #
        #     self.after_quit()
        #     self.login_main()

        time.sleep(1)
        cookies = self.driver.get_cookies()
        cookie_name = list()
        for cookie in cookies:
            cookie_name.append(cookie['name'])

        if 'TDC_itoken' in cookie_name:
            return '验证失败'

        self.agent = self.driver.execute_script("return navigator.userAgent")

        self.after_quit()
        return cookies


if __name__ == '__main__':
    def restart(times):
        print(times)
        times = times + 1
        try:
            login = Login()
            res = login.login_main()
            print(res)
            exit(123)
            cookies = json.dumps(login.login_main())

            db = pymysql.connect("localhost", "root", "123456", "kanban")
            cursor = db.cursor()
            create_at = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
            # 插入数据
            sql = 'INSERT INTO cookie(account, password, agent,cookie, update_at, create_at) VALUES (%s, %s, %s, %s,  %s,  %s)'

            reCount = cursor.execute(sql, (login.account, login.password, login.agent, cookies, create_at, create_at))
            db.commit()
            db.close()
            return '验证失败'
        except EOFError as e:
            print(e)

        finally:
            if times <= 3:
                restart(times)
            else:
                return "验证结束"
                exit(213)
    print(restart(1))